// g++ testffmpeg.cpp -lavformat -lavcodec -lavutil
extern "C"
{
#include <libavformat/avformat.h>
#include <libavutil/timestamp.h>
#include <libavcodec/avcodec.h>
//#include <libswscale/swscale.h>
#include <libswresample/swresample.h>
#include <libavutil/avassert.h>
#include <libavutil/audio_fifo.h>
}
#include <iostream>

// 滤镜
void test_video_filter()
{
}

void test_audio_filter()
{
}

// 解码
void test_transcoding(int argc, char *argv[])
{
}

void test_video_decode()
{
}

void test_video_encode()
{
}

void test_audio_decode()
{
}

void test_audio_encode()
{
}

void test_avio()
{
}

// 截取
void test_cuteout()
{
}

/*
 * 打印包信息
 * @param fmt_ctx 封装上下文
 * @param pkt 要打印信息的包
 * @param tag 包标签
 */
void log_packet(const AVFormatContext *fmt_ctx, const AVPacket *pkt, const char *tag)
{
    AVRational *time_base = &fmt_ctx->streams[pkt->stream_index]->time_base;

    char tmp[AV_TS_MAX_STRING_SIZE] = {0};
    printf("%s: pts:%s pts_time:%s dts:%s dts_time:%s duration:%s duration_time:%s stream_index:%d\n",
           tag,
           av_ts_make_string(tmp, pkt->pts), av_ts_make_time_string(tmp, pkt->pts, time_base),
           av_ts_make_string(tmp, pkt->dts), av_ts_make_time_string(tmp, pkt->dts, time_base),
           av_ts_make_string(tmp, pkt->duration), av_ts_make_time_string(tmp, pkt->duration, time_base),
           pkt->stream_index);
}

// 转换封装
// ./a.out /home/ljd/Videos/跑步机遛猫.mp4 /home/ljd/Videos/跑步机遛猫.flv
int test_transform(int argc, char **argv)
{
    if (argc < 3)
    {
        printf("usage: %s input output\n"
               "API example program to remux a media file with libavformat and libavcodec.\n"
               "The output format is guessed according to the file extension.\n"
               "\n",
               argv[0]);
        return 1;
    }

    const char *in_filename = argv[1];
    const char *out_filename = argv[2];

    // 打开输入文件
    AVFormatContext *ifmt_ctx = NULL;
    int ret;
    char tmp[AV_ERROR_MAX_STRING_SIZE] = {0};
    if ((ret = avformat_open_input(&ifmt_ctx, in_filename, NULL, NULL)) < 0)
    {
        fprintf(stderr, "Could not open input file '%s':%s", in_filename, av_make_error_string(tmp, AV_ERROR_MAX_STRING_SIZE, ret));
        return -1;
    }

    // 获取流信息
    if ((ret = avformat_find_stream_info(ifmt_ctx, 0)) < 0)
    {
        fprintf(stderr, "Failed to retrieve input stream information:%s\n", av_make_error_string(tmp, AV_ERROR_MAX_STRING_SIZE, ret));

        avformat_close_input(&ifmt_ctx);
        return -1;
    }

    // 打印输入流信息
    av_dump_format(ifmt_ctx, 0, in_filename, 0);

    // 创建输出上下文。不输入格式定时和格式名称，而是从文件后缀得到
    AVFormatContext *ofmt_ctx = NULL;
    avformat_alloc_output_context2(&ofmt_ctx, NULL, NULL, out_filename);
    if (!ofmt_ctx)
    {
        fprintf(stderr, "Could not create output context\n");

        avformat_close_input(&ifmt_ctx);
        return -1;
    }

    // 流数量
    auto stream_mapping_size = ifmt_ctx->nb_streams;

    // 用于存储流序号的数组
    auto stream_mapping = (int *)av_calloc(stream_mapping_size, sizeof(int));
    if (!stream_mapping)
    {
        avformat_close_input(&ifmt_ctx);
        avformat_free_context(ofmt_ctx);
        return -1;
    }

    // 输出封装格式
    auto ofmt = ofmt_ctx->oformat;

    // 处理各个流
    int stream_index = 0;
    for (auto i = 0; i < ifmt_ctx->nb_streams; i++)
    {
        auto in_stream = ifmt_ctx->streams[i];
        // 参数
        auto in_codecpar = in_stream->codecpar;

        // 排除非音频、视频和字幕的流
        if (in_codecpar->codec_type != AVMEDIA_TYPE_AUDIO &&
            in_codecpar->codec_type != AVMEDIA_TYPE_VIDEO &&
            in_codecpar->codec_type != AVMEDIA_TYPE_SUBTITLE)
        {
            stream_mapping[i] = -1;
            continue;
        }

        // 流序号
        stream_mapping[i] = stream_index++;

        // 在输出文件上下文中创建输出流
        auto out_stream = avformat_new_stream(ofmt_ctx, NULL);
        if (!out_stream)
        {
            fprintf(stderr, "Failed allocating output stream\n");

            avformat_close_input(&ifmt_ctx);
            avformat_free_context(ofmt_ctx);

            av_freep(&stream_mapping);
            return -1;
        }

        // 复制流参数
        ret = avcodec_parameters_copy(out_stream->codecpar, in_codecpar);
        if (ret < 0)
        {
            fprintf(stderr, "Failed to copy codec parameters:%s\n", av_make_error_string(tmp, AV_ERROR_MAX_STRING_SIZE, ret));

            avformat_close_input(&ifmt_ctx);
            avformat_free_context(ofmt_ctx);

            av_freep(&stream_mapping);
            return -1;
        }

        out_stream->codecpar->codec_tag = 0;
    }

    // 打印输出流信息
    av_dump_format(ofmt_ctx, 0, out_filename, 1);

    if (!(ofmt->flags & AVFMT_NOFILE))
    {
        // 创建 IO 上下文，用于访问文件
        ret = avio_open(&ofmt_ctx->pb, out_filename, AVIO_FLAG_WRITE);
        if (ret < 0)
        {
            fprintf(stderr, "Could not open output file '%s':%s", out_filename, av_make_error_string(tmp, AV_ERROR_MAX_STRING_SIZE, ret));

            avformat_close_input(&ifmt_ctx);
            avformat_free_context(ofmt_ctx);

            av_freep(&stream_mapping);
            return -1;
        }
    }

    // 写入文件封装头
    ret = avformat_write_header(ofmt_ctx, NULL);
    if (ret < 0)
    {
        fprintf(stderr, "Error occurred when opening output file:%s\n", av_make_error_string(tmp, AV_ERROR_MAX_STRING_SIZE, ret));

        avformat_close_input(&ifmt_ctx);

        /* close output */
        if (ofmt_ctx && !(ofmt->flags & AVFMT_NOFILE))
            avio_closep(&ofmt_ctx->pb);
        avformat_free_context(ofmt_ctx);

        av_freep(&stream_mapping);
        return -1;
    }

    // 创建一个帧对象
    auto pkt = av_packet_alloc();
    if (!pkt)
    {
        fprintf(stderr, "Could not allocate AVPacket\n");

        avformat_close_input(&ifmt_ctx);

        /* close output */
        if (ofmt_ctx && !(ofmt->flags & AVFMT_NOFILE))
            avio_closep(&ofmt_ctx->pb);
        avformat_free_context(ofmt_ctx);

        av_freep(&stream_mapping);
        return -1;
    }

    // 读取并处理帧
    while ((ret = av_read_frame(ifmt_ctx, pkt)) == 0)
    {
        // 输入流
        auto in_stream = ifmt_ctx->streams[pkt->stream_index];

        // 忽略未映射的流
        if (pkt->stream_index >= stream_mapping_size ||
            stream_mapping[pkt->stream_index] < 0)
        {
            av_packet_unref(pkt);
            continue;
        }

        // 输出流
        pkt->stream_index = stream_mapping[pkt->stream_index];
        auto out_stream = ofmt_ctx->streams[pkt->stream_index];

        log_packet(ifmt_ctx, pkt, "in");

        // 复制帧
        av_packet_rescale_ts(pkt, in_stream->time_base, out_stream->time_base);
        pkt->pos = -1;

        log_packet(ofmt_ctx, pkt, "out");

        // 将帧写到输出文件中
        ret = av_interleaved_write_frame(ofmt_ctx, pkt);
        /* pkt is now blank (av_interleaved_write_frame() takes ownership of
         * its contents and resets pkt), so that no unreferencing is necessary.
         * This would be different if one used av_write_frame(). */
        if (ret < 0)
        {
            fprintf(stderr, "Error muxing packet:%s\n", av_make_error_string(tmp, AV_ERROR_MAX_STRING_SIZE, ret));
            break;
        }
    }

    // 写入文件封装尾
    av_write_trailer(ofmt_ctx);

    av_packet_free(&pkt);
    return 0;
}

// 解封装
void test_unpacking()
{
    av_register_all();

    AVFormatContext *fmt_ctx = NULL;
    auto ret = avformat_open_input(&fmt_ctx, "/home/ljd/Videos/跑步机遛猫.mp4", NULL, NULL);
    if (ret != 0)
    {
        std::cout << "could not open file" << std::endl;
        return;
    }

    ret = avformat_find_stream_info(fmt_ctx, NULL);
    if (ret < 0)
    {
        std::cout << "could not find stream infomation" << std::endl;
        return;
    }

    AVPacket pack = {0};
    av_init_packet(&pack);

    while (av_read_frame(fmt_ctx, &pack) >= 0)
    {
        auto origin = pack;

        // 处理帧
        do
        {

        } while (pack.size > 0);

        av_packet_unref(&origin);
    }

    avformat_close_input(&fmt_ctx);
}

// 封装
void test_packing()
{
    // 初始化 libavformat，注册所有 muxers, demuxers 和协议。
    // 如果不调用此函数，则必须精确选择要支持哪种格式。
    // av_register_all();

    // AVFormatContext context;

    // auto avs = avformat_new_stream(&context, NULL);

    // // 容器头
    // auto ret = avformat_write_header(&context, );

    // // 内容
    // AVPacket pack;
    // av_init_packet(&pack);

    // // 容器尾
    // ret = av_write_trailer(&context);
}

int main(int argc, char *argv[])
{
    // 封装
    // test_packing();
    // test_unpacking();
    test_transform(argc, argv);
    // test_cuteout();
    // test_avio();

    // // 编码
    // test_audio_encode();
    // test_audio_decode();
    // test_video_encode();
    // test_video_decode();
    test_transcoding(argc, argv);

    // // 滤镜
    // test_audio_filter();
    // test_video_filter();

    return 0;
}

/*
ffmpeg 命令

转封装


转码


流媒体


滤镜


采集设备


*/
